package besta.moon.engine.selector;

import besta.moon.engine.RowSource;
import besta.moon.datastruct.LongList;
import besta.moon.MoonException;
import besta.moon.MoonCode;
import besta.moon.sql.expression.Expressions;

public class Scrollable extends RowSource {

    private final RowSource rowSource;

    private int rowIdx;

    private final LongList rowList = new LongList();

    public 
            Scrollable(RowSource rowSource) {
        this.rowSource = rowSource;
    }

    @Override
    public final boolean isScrollable() {
        return true;
    }

    @Override
    public void beforeFirst() throws Exception {
        rowIdx = -1;
        rowSource.beforeFirst();
    }

    @Override
    public boolean isBeforeFirst() {
        return rowIdx == -1 || rowList.size() == 0;
    }

    @Override
    public boolean isFirst() {
        return rowIdx == 0 && rowList.size() > 0;
    }

    @Override
    public boolean first() throws Exception {
        rowIdx = -1;
        return next();
    }

    @Override
    public boolean previous() throws Exception {
        if (rowIdx > -1) {
            rowIdx--;
            if (rowIdx > -1 && rowIdx < rowList.size()) {
                rowSource.setRowPosition(rowList.get(rowIdx));
                return true;
            }
        }
        rowSource.beforeFirst();
        return false;
    }

    @Override
    public boolean next() throws Exception {
        if (++rowIdx < rowList.size()) {
            rowSource.setRowPosition(rowList.get(rowIdx));
            return true;
        }
        final boolean result = rowSource.next();
        if (result) {
            rowList.add(rowSource.getRowPosition());
            return true;
        }
        rowIdx = rowList.size(); 
        return false;
    }

    @Override
    public boolean last() throws Exception {
        afterLast();
        return previous();
    }

    @Override
    public boolean isLast() throws Exception {
        if (rowIdx + 1 != rowList.size()) {
            return false;
        }
        boolean isNext = next();
        previous();
        return !isNext && (rowIdx + 1 == rowList.size() && rowList.size() > 0);
    }

    @Override
    public boolean isAfterLast() throws Exception {
        if (rowIdx >= rowList.size()) {
            return true;
        }
        if (isBeforeFirst() && rowList.size() == 0) {
            next();
            previous();
            if (rowList.size() == 0) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void afterLast() throws Exception {
        if (rowIdx + 1 < rowList.size()) {
            rowIdx = rowList.size() - 1;
            rowSource.setRowPosition(rowList.get(rowIdx));
        }
        while (next()) {
        }
    }

    @Override
    public boolean absolute(int row) throws Exception {
        if (row == 0) {
            throw MoonException.create(MoonCode.ROW_0_ABSOLUTE);
        }
        if (row < 0) {
            afterLast();
            rowIdx = rowList.size() + row;
            if (rowIdx < 0) {
                beforeFirst();
                return false;
            } else {
                rowSource.setRowPosition(rowList.get(rowIdx));
                return true;
            }
        }
        if (row <= rowList.size()) {
            rowIdx = row - 1;
            rowSource.setRowPosition(rowList.get(rowIdx));
            return true;
        }

        rowIdx = rowList.size() - 1;
        if (rowIdx >= 0) {
            rowSource.setRowPosition(rowList.get(rowIdx));
        }
        boolean result;
        while ((result = next()) && row - 1 > rowIdx) {
        }
        return result;
    }

    @Override
    public boolean relative(int rows) throws Exception {
        int newRow = rows + rowIdx + 1;
        if (newRow <= 0) {
            beforeFirst();
            return false;
        } else {
            return absolute(newRow);
        }
    }

    @Override
    public int getRow() throws Exception {
        if (rowIdx >= rowList.size()) {
            return 0;
        }
        return rowIdx + 1;
    }

    @Override
    public long getRowPosition() {
        return rowIdx;
    }

    @Override
    public void setRowPosition(long rowPosition) throws Exception {
        rowIdx = (int) rowPosition;
    }

    @Override
    public final boolean rowInserted() {
        return rowSource.rowInserted();
    }

    @Override
    public final boolean rowDeleted() {
        return rowSource.rowDeleted();
    }

    @Override
    public void nullRow() {
        rowSource.nullRow();
        rowIdx = -1;
    }

    @Override
    public void noRow() {
        rowSource.noRow();
        rowIdx = -1;
    }

    @Override
    public void execute() throws Exception {
        rowSource.execute();
        rowList.clear();
        rowIdx = -1;
    }

    @Override
    public boolean isExpressionsFromThisRowSource(Expressions columns) {
        return rowSource.isExpressionsFromThisRowSource(columns);
    }
}
